#include <windows.h>
#include <iostream>
#include <fstream>
#include <GL/glut.h>
#include "utility.h"
#include "mesh.h"

int WINDOW_WIDTH = 500, WINDOW_HEIGHT = 500;

// mouse state
int prevX = 0, prevY = 0;
bool leftPressed = false, rightPressed = false, middlePressed = false;

// view state
float rotMat [ 16 ] = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
float focus [ 3 ] = {0,0,-1.5};

Mesh mesh;

void draw_triangles (Point point[3], GLfloat color [40])
{

	Vector v1 = point[1] - point[0];
	Vector v2 = point[2] - point[0];
	Vector normal = v1^v2 ;

	glBegin(GL_TRIANGLES);
	glNormal3f(normal.x, normal.y, normal.z); 
	glVertex3f(point[0].x, point[0].y, point[0].z);
	glVertex3f(point[1].x, point[1].y, point[1].z);
	glVertex3f(point[2].x, point[2].y, point[2].z);
	glEnd();
	glFlush();

}

void draw_faces (unsigned idx) 
{
	Face * face = mesh.v_faces[idx] ;

	GLfloat color1 [60] ;


	Point p[3];

	p[0] = mesh.v_faces[idx]->he->from_v->point ;
	p[1] = mesh.v_faces[idx]->he->next->from_v->point ;
	p[2] = mesh.v_faces[idx]->he->next->next->from_v->point ;
	int_RGB color = mesh.v_faces[idx]->color ;


	Point n = Point::normal((p[1]-p[0])^(p[2]-p[0])) ;
	glNormal3f (n.x, n.y, n.z);

	draw_triangles(p, color1 );

}
Vector3 face_normal (Face * face)
{
	Point point[3];
	point[0] = face->he->from_v->point ;
	point[1] = face->he->next->from_v->point ;
	point[2] = face->he->next->next->from_v->point ;

	Vector v1 = point[1] - point[0];
	Vector v2 = point[2] - point[0];
	Vector normal = v1^v2 ;
	return normal ; 
}
static Vector3 screenZVector ()
{
	double mvmatrix[16];
	double projmatrix[16];
	int viewport[4];
	double dX, dY, dZ, dClickY; // glUnProject uses doubles, but I'm using floats for these 3D vectors

	glGetIntegerv(GL_VIEWPORT, viewport);	
	glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
	glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
	//dClickY = double (g_WindowHeight - y); // OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top
	dClickY = 300;
	double x = 250;
	gluUnProject ((double) x, dClickY, 0.0, mvmatrix, projmatrix, viewport, &dX, &dY, &dZ);
	Vector3 ClickRayP1 = Vector3 ( (float) dX, (float) dY, (float) dZ );
	//gluUnProject ((double) x, dClickY, 1.0, mvmatrix, projmatrix, viewport, &dX, &dY, &dZ);

	//Vector3 ClickRayP2 = Vector3 ( (float) dX, (float) dY, (float) dZ );
	return ClickRayP1 ;
}

Vector3 mappingObj2Win (Vector3 point, 	double mvmatrix[16],  double projmatrix[16],  int viewport[4])
{

	double winx, winy, winz; 

	gluProject (point.x, point.y, point.z, mvmatrix, projmatrix, viewport, & winx, & winy, & winz);
	Vector3 result;
	
	result.x = winx;
	result.y = winy;
	result.z = 0.0f;
	return result;
}
void display() 
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Set up viewing matrices
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60, float(WINDOW_WIDTH)/WINDOW_HEIGHT, .0001, 100);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//Camera
	glTranslatef(focus[0], focus[1], focus[2]);
	glMultMatrixf(rotMat);

	Vector3 v = screenZVector();
	// drawing code goes here
	int size = mesh.v_faces.size();
	double mvmatrix[16];
	double projmatrix[16];  
	int viewport[4];
	glGetIntegerv(GL_VIEWPORT, viewport);	
	glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
	glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
	for (int i = 0; i < mesh.v_faces.size() ; ++i)
	{
		Point p[3];
		p[0] = mappingObj2Win(mesh.v_faces[i]->he->from_v->point, mvmatrix, projmatrix, viewport);
		p[1] = mappingObj2Win(mesh.v_faces[i]->he->next->from_v->point, mvmatrix, projmatrix, viewport);
		p[2] = mappingObj2Win(mesh.v_faces[i]->he->next->next->from_v->point, mvmatrix, projmatrix, viewport);
		
		Vector v1 = p[1]-p[0];
		Vector v2 = p[2]-p[0];
		Vector v3 = v1^v2 ;
		if (v3.z > 0)
		{
			draw_faces (i);
		}
	}
	// end drawing code
	glutSwapBuffers();
}

void reshape(int width, int height)
{
	glViewport(0, 0, width, height);
	WINDOW_WIDTH = width;
	WINDOW_HEIGHT = height;
}

void mouse(int button, int state, int x, int y)
{
	y = WINDOW_HEIGHT - y;

	// Mouse state that should always be stored on pressing
	if (state == GLUT_DOWN)
	{
		prevX = x;
		prevY = y;
	}

	if (button == GLUT_LEFT_BUTTON)
	{
		leftPressed = state == GLUT_DOWN;
	}

	if (button == GLUT_RIGHT_BUTTON)
	{
		rightPressed = state == GLUT_DOWN;
	}

	if (button == GLUT_MIDDLE_BUTTON)
	{
		middlePressed = state == GLUT_DOWN;
	}
}

void motion(int x, int y)
{
	y = WINDOW_HEIGHT - y;

	float dx = (x - prevX);
	float dy = (y - prevY);

	// rotate the scene
	if (leftPressed)
	{
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glRotatef(dx, 0, 1, 0);
		glRotatef(dy, -1, 0, 0);
		glMultMatrixf(rotMat);
		glGetFloatv(GL_MODELVIEW_MATRIX, rotMat);
	}
	else if (middlePressed)
	{
		focus [ 0 ] += 0.005 * dx;
		focus [ 1 ] += 0.005 * dy;
	}
	else if (rightPressed)
	{
		focus [ 2 ] += 0.01 * dy;
	}

	// Store previous mouse positions
	prevX = x;
	prevY = y;

	glutPostRedisplay();
}


void keyboard(unsigned char key, int x, int y)
{
	glutPostRedisplay();
}


void init(void)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

	glClearColor(0, 0, 0, 1);   
	glEnable(GL_DEPTH_TEST);

	// set up light colors (ambient, diffuse, specular)
	GLfloat lightAbmient[] = {.5f, .5f, .5f, 1.0f};     
	GLfloat lightDiffuse[] = {.9f, .9f, .9f, 1.0f};     
	GLfloat lightSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f};             
	glLightfv(GL_LIGHT0, GL_AMBIENT, lightAbmient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);

	// position the light
	float lightPos[4] = {0, 10, 10, 0};
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
	glEnable(GL_LIGHT0);   

	//glEnable(GL_LIGHT0);
	//glEnable(GL_LIGHTING);


	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	//glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);

	//glEnable(GL_CULL_FACE);
}


int main(int argc, char **argv)
{

	std::ifstream ifs ("D:\\workspace\\data\\cube3.obj");
	ifs >> mesh ;
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowPosition(0, 0);
	glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
	glutCreateWindow("CSCE 645 - Your Name Here");

	init();

	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);

	// main loop
	glutMainLoop();
}